Execute: Cmd+Shift+Enter. New Chunk: Cmd+Option+I. Preview: Cmd+Shift+K Notes: The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed. # Introduction

0.1 Configuration

Loading required package: lattice
Loading required package: ggplot2
Loading required package: nlme
This is mgcv 1.8-26. For overview type 'help("mgcv-package")'.

Attaching package: ‘dplyr’

The following object is masked from ‘package:nlme’:

    collapse

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
corrplot 0.84 loaded

0.2 System Information

Due to the large number of libraries in use I have provided system information.

R version 3.5.1 (2018-07-02)
Platform: x86_64-apple-darwin13.4.0 (64-bit)
Running under: macOS High Sierra 10.13.6

Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /anaconda3/lib/R/lib/libRblas.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] htmltools_0.3.6    corrplot_0.84      plotly_4.8.0       RColorBrewer_1.1-2 ggExtra_0.8       
 [6] dplyr_0.8.0.1      DataExplorer_0.7.0 mgcv_1.8-26        nlme_3.1-137       caret_6.0-81      
[11] ggplot2_3.1.0      lattice_0.20-38    leaps_3.0         

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.0         lubridate_1.7.4    tidyr_0.8.2        class_7.3-15       assertthat_0.2.0  
 [6] digest_0.6.18      ipred_0.9-8        foreach_1.4.4      mime_0.6           R6_2.4.0          
[11] plyr_1.8.4         stats4_3.5.1       evaluate_0.12      httr_1.4.0         pillar_1.3.1      
[16] rlang_0.3.1        lazyeval_0.2.1     rstudioapi_0.9.0   data.table_1.12.0  miniUI_0.1.1.1    
[21] rpart_4.1-13       Matrix_1.2-15      rmarkdown_1.11     splines_3.5.1      gower_0.1.2       
[26] stringr_1.4.0      htmlwidgets_1.3    igraph_1.2.4       munsell_0.5.0      shiny_1.2.0       
[31] httpuv_1.4.5.1     compiler_3.5.1     xfun_0.4           pkgconfig_2.0.2    nnet_7.3-12       
[36] tidyselect_0.2.5   tibble_2.0.1       gridExtra_2.3      prodlim_2018.04.18 codetools_0.2-16  
[41] viridisLite_0.3.0  later_0.8.0        crayon_1.3.4       withr_2.1.2        MASS_7.3-51.1     
[46] recipes_0.1.4      ModelMetrics_1.1.0 grid_3.5.1         xtable_1.8-3       jsonlite_1.6      
[51] gtable_0.2.0       magrittr_1.5       scales_1.0.0       stringi_1.3.1      reshape2_1.4.3    
[56] promises_1.0.1     timeDate_3043.102  generics_0.0.2     lava_1.6.5         iterators_1.0.10  
[61] tools_3.5.1        glue_1.3.0         purrr_0.2.5        networkD3_0.4      parallel_3.5.1    
[66] survival_2.43-3    yaml_2.2.0         colorspace_1.4-0   knitr_1.21        
     repr IRdisplay  IRkernel 
 "0.19.2"   "0.7.0"  "0.8.15" 

1 Data

First we inspect the raw records using bash. Always a good idea to look at things before you mash ’em into an IDE.

"","Sales","CompPrice","Income","Advertising","Population","Price","ShelveLoc","Age","Education","Urban","US"
"1",9.5,138,73,11,276,120,"Bad",42,17,"Yes","Yes"
"2",11.22,111,48,16,260,83,"Good",65,10,"Yes","Yes"
"3",10.06,113,35,10,269,80,"Medium",59,12,"Yes","Yes"
"4",7.4,117,100,4,466,97,"Medium",55,14,"Yes","Yes"

Now I load the data into r, and drop the “ID” column.

Here I create two new dataframes to manage numeric and categorical data.

Let’s get some quick summaries of each:

[1] "Numeric Summaries"
     Sales          CompPrice       Income        Advertising       Population        Price            Age       
 Min.   : 0.000   Min.   : 77   Min.   : 21.00   Min.   : 0.000   Min.   : 10.0   Min.   : 24.0   Min.   :25.00  
 1st Qu.: 5.390   1st Qu.:115   1st Qu.: 42.75   1st Qu.: 0.000   1st Qu.:139.0   1st Qu.:100.0   1st Qu.:39.75  
 Median : 7.490   Median :125   Median : 69.00   Median : 5.000   Median :272.0   Median :117.0   Median :54.50  
 Mean   : 7.496   Mean   :125   Mean   : 68.66   Mean   : 6.635   Mean   :264.8   Mean   :115.8   Mean   :53.32  
 3rd Qu.: 9.320   3rd Qu.:135   3rd Qu.: 91.00   3rd Qu.:12.000   3rd Qu.:398.5   3rd Qu.:131.0   3rd Qu.:66.00  
 Max.   :16.270   Max.   :175   Max.   :120.00   Max.   :29.000   Max.   :509.0   Max.   :191.0   Max.   :80.00  
   Education   
 Min.   :10.0  
 1st Qu.:12.0  
 Median :14.0  
 Mean   :13.9  
 3rd Qu.:16.0  
 Max.   :18.0  
[1] "Categorical Summaries"
  ShelveLoc   Urban       US     
 Bad   : 96   No :118   No :142  
 Good  : 85   Yes:282   Yes:258  
 Medium:219                      
'data.frame':   400 obs. of  8 variables:
 $ Sales      : num  9.5 11.22 10.06 7.4 4.15 ...
 $ CompPrice  : int  138 111 113 117 141 124 115 136 132 132 ...
 $ Income     : int  73 48 35 100 64 113 105 81 110 113 ...
 $ Advertising: int  11 16 10 4 3 13 0 15 0 0 ...
 $ Population : int  276 260 269 466 340 501 45 425 108 131 ...
 $ Price      : int  120 83 80 97 128 72 108 120 124 124 ...
 $ Age        : int  42 65 59 55 38 78 71 67 76 76 ...
 $ Education  : int  17 10 12 14 13 16 15 10 10 17 ...
'data.frame':   400 obs. of  3 variables:
 $ ShelveLoc: Factor w/ 3 levels "Bad","Good","Medium": 1 2 3 3 1 1 3 2 3 3 ...
 $ Urban    : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 2 2 1 1 ...
 $ US       : Factor w/ 2 levels "No","Yes": 2 2 2 2 1 2 1 2 1 2 ...

1.1 Data Dimensionality

This command is to inspect the different data types in the data.

'data.frame':   400 obs. of  11 variables:
 $ Sales      : num  9.5 11.22 10.06 7.4 4.15 ...
 $ CompPrice  : int  138 111 113 117 141 124 115 136 132 132 ...
 $ Income     : int  73 48 35 100 64 113 105 81 110 113 ...
 $ Advertising: int  11 16 10 4 3 13 0 15 0 0 ...
 $ Population : int  276 260 269 466 340 501 45 425 108 131 ...
 $ Price      : int  120 83 80 97 128 72 108 120 124 124 ...
 $ ShelveLoc  : Factor w/ 3 levels "Bad","Good","Medium": 1 2 3 3 1 1 3 2 3 3 ...
 $ Age        : int  42 65 59 55 38 78 71 67 76 76 ...
 $ Education  : int  17 10 12 14 13 16 15 10 10 17 ...
 $ Urban      : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 2 2 1 1 ...
 $ US         : Factor w/ 2 levels "No","Yes": 2 2 2 2 1 2 1 2 1 2 ...
[1] ""
[1] "Number of Columns: 11"
[1] "Number of Numeric Columns: 8"
[1] "Number of Categorical Columns: 3"

Here’s a quick way to examine general properties of the data:

2 Numeric Plotting

I start out with a few general scatter plots.

Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()

This plot below shows good separation and a weak linear trend. These variables are worth investigating.

Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()

Here we inspect the density of the ‘Price vs Sales’ relationship:

Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.

3 Normalization

I choose to normalize the numeric data in order to be able to plot each variable on the same scale. This will allow me to investigate the variation of each predictor relaticve to Sales.

'data.frame':   400 obs. of  8 variables:
 $ Sales      : num  0.7095 1.3185 0.9078 -0.0341 -1.1849 ...
 $ CompPrice  : num  0.849 -0.911 -0.781 -0.52 1.045 ...
 $ Income     : num  0.155 -0.738 -1.203 1.12 -0.166 ...
 $ Advertising: num  0.656 1.408 0.506 -0.396 -0.547 ...
 $ Population : num  0.0757 -0.0328 0.0282 1.3649 0.51 ...
 $ Price      : num  0.178 -1.385 -1.512 -0.794 0.515 ...
 $ Age        : num  -0.699 0.721 0.35 0.104 -0.946 ...
 $ Education  : num  1.183 -1.4882 -0.725 0.0382 -0.3434 ...
[1] ""
     Sales            CompPrice            Income          Advertising        Population           Price         
 Min.   :-2.65440   Min.   :-3.12856   Min.   :-1.70290   Min.   :-0.9977   Min.   :-1.72918   Min.   :-3.87702  
 1st Qu.:-0.74584   1st Qu.:-0.65049   1st Qu.:-0.92573   1st Qu.:-0.9977   1st Qu.:-0.85387   1st Qu.:-0.66711  
 Median :-0.00224   Median : 0.00163   Median : 0.01224   Median :-0.2459   Median : 0.04858   Median : 0.05089  
 Mean   : 0.00000   Mean   : 0.00000   Mean   : 0.00000   Mean   : 0.0000   Mean   : 0.00000   Mean   : 0.00000  
 3rd Qu.: 0.64575   3rd Qu.: 0.65375   3rd Qu.: 0.79834   3rd Qu.: 0.8067   3rd Qu.: 0.90693   3rd Qu.: 0.64219  
 Max.   : 3.10670   Max.   : 3.26225   Max.   : 1.83458   Max.   : 3.3630   Max.   : 1.65671   Max.   : 3.17633  
      Age             Education       
 Min.   :-1.74827   Min.   :-1.48825  
 1st Qu.:-0.83779   1st Qu.:-0.72504  
 Median : 0.07268   Median : 0.03816  
 Mean   : 0.00000   Mean   : 0.00000  
 3rd Qu.: 0.78255   3rd Qu.: 0.80137  
 Max.   : 1.64673   Max.   : 1.56457  

3.1 Distributions

Here are scaled distributions (histograms and density plots) for each numeric variable, including Sales. The variables relating to money ($) tend to be approximately normal. Many other variables tend to be approximately uniform, which does not bode well for their predictive power.

Here we plot all numeric variables against their distributions. This is just another way to examine the information shown above.

Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()

3.2 Scatterplots

Here we plot all numeric variables against Sales (scaled). This allows us to investigate possible linear relationships between that variable and Sales. As shown below, only ‘Price’ appears to have a linear relationshop worth investigating. This took me so long to figure out.

`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.`line.width` does not currently support multiple values.

By adding naive regression lines to a few scatterplots we can confirm our suspicions:

fit.Pop <- lm(Sales ~ Population, data = scaled.nums)
fit.Age <- lm(Sales ~ Age, data = scaled.nums)
fit.CompPrice <- lm(Sales ~ CompPrice, data = scaled.nums)
fit.Price <- lm(Sales ~ Price, data = scaled.nums)
regression.scatterplots <- htmltools::tagList()
regression.scatterplots[[1]] <-  plot_ly(scaled.nums, 
          x = ~Population,
          name = 'Population vs Sales Regression Line') %>% 
              add_markers(y = ~Sales,
                    name = 'Population vs Sales Observations') %>% 
                            add_lines(x = ~Population, 
                                  y = fitted(fit.Pop)) %>%
                                        layout(title = "Population vs Sales",
                                               yaxis=list(title='Sales',
                                               xaxis=list(title='Population')),
                                               showlegend = FALSE)
                                              
           
regression.scatterplots[[2]] <-  plot_ly(scaled.nums, 
          x = ~Age,
          name = 'Age vs Sales Regression Line') %>% 
              add_markers(y = ~Sales,
                    name = 'Age vs Sales Observations') %>% 
                            add_lines(x = ~Age, 
                                  y = fitted(fit.Age)) %>%
                                        layout(title = "Age vs Sales",
                                               yaxis=list(title='Sales',
                                               xaxis=list(title='Age')),
                                               showlegend = FALSE)
regression.scatterplots[[3]] <-  plot_ly(scaled.nums, 
          x = ~CompPrice,
          name = 'CompPrice vs Sales Regression Line') %>% 
              add_markers(y = ~Sales,
                    name = 'CompPrice vs Sales Observations') %>% 
                            add_lines(x = ~CompPrice, 
                                  y = fitted(fit.CompPrice)) %>%
                                        layout(title = "CompPrice vs Sales",
                                               yaxis=list(title='Sales',
                                               xaxis=list(title='CompPrice')),
                                               showlegend = FALSE)
regression.scatterplots[[4]] <-  plot_ly(scaled.nums, 
          x = ~Price,
          name = 'Price vs Sales Regression Line') %>% 
              add_markers(y = ~Sales,
                    name = 'Price vs Sales Observations') %>% 
                            add_lines(x = ~Price, 
                                  y = fitted(fit.Price)) %>%
                                        layout(title = "Price vs Sales",
                                               yaxis=list(title='Sales',
                                               xaxis=list(title='Price')),
                                               showlegend = FALSE)
regression.scatterplots

Let’s compare the slopes:

Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()

Here’s a pretty graphic that doesn’t help me understand anything about the data.

No trace type specified and no positional attributes specifiedNo trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plot.ly/r/reference/#scatter
No scatter mode specifed:
  Setting the mode to markers
  Read more about this attribute -> https://plot.ly/r/reference/#scatter-mode
Specifying width/height in layout() is now deprecated.
Please specify in ggplotly() or plot_ly()

4 Categorical Plotting

First, let’s create a dataframe that we can use:

Here we can see all categorical by Sales. We suspected that ‘ShelveLoc’ would be important based on one of the early scatterplots. It seems that this is the case.

Here’s the same thing, but more musically:

5 Linear Regression

First, let’s merge the data set into a single dataframe

First, let’s look at some things that may give us trouble. Luckily it looks like the only serious correlation is with our dependent variable.

It appears that residuals are roughly symmetrical around 0. That’s strange. Mostly due to a relatively poor overall fit. Note how close to zero most of the coefficient estimates are.


Call:
lm(formula = Sales ~ ., data = scaled.merged)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.32467 -0.29881 -0.01677  0.27320  1.82851 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)     -0.70000    0.06018 -11.633  < 2e-16 ***
CompPrice        0.44976    0.02515  17.883  < 2e-16 ***
Income           0.11240    0.02029   5.539 4.78e-08 ***
Advertising      0.24069    0.02389  10.076  < 2e-16 ***
Population       0.01656    0.02134   0.776    0.438    
Price           -0.70235    0.02530 -27.759  < 2e-16 ***
Age             -0.23287    0.02012 -11.576  < 2e-16 ***
Education       -0.02619    0.02029  -1.291    0.197    
ShelveLocGood    1.39901    0.06043  23.152  < 2e-16 ***
ShelveLocMedium  0.59700    0.04974  12.003  < 2e-16 ***
UrbanYes         0.03785    0.04353   0.870    0.385    
USYes            0.06868    0.04773   1.439    0.151    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4672 on 538 degrees of freedom
Multiple R-squared:  0.7543,    Adjusted R-squared:  0.7492 
F-statistic: 150.1 on 11 and 538 DF,  p-value: < 2.2e-16

6 Interaction Terms

Here we define a new model with some interaction terms: a. Income and Advertising b. Income and CompPrice c. Price and Age


Call:
lm(formula = Sales ~ . + Income * Advertising + Income * CompPrice + 
    Price * Age, data = scaled.merged)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.31721 -0.30723 -0.01874  0.28583  1.91559 

Coefficients:
                   Estimate Std. Error t value Pr(>|t|)    
(Intercept)        -0.69732    0.05993 -11.635  < 2e-16 ***
CompPrice           0.44981    0.02500  17.991  < 2e-16 ***
Income              0.10889    0.02028   5.369 1.18e-07 ***
Advertising         0.23790    0.02377  10.008  < 2e-16 ***
Population          0.01367    0.02121   0.645   0.5194    
Price              -0.70114    0.02513 -27.903  < 2e-16 ***
Age                -0.23142    0.02003 -11.553  < 2e-16 ***
Education          -0.03038    0.02024  -1.501   0.1339    
ShelveLocGood       1.39095    0.06040  23.028  < 2e-16 ***
ShelveLocMedium     0.58444    0.04973  11.752  < 2e-16 ***
UrbanYes            0.03659    0.04323   0.846   0.3977    
USYes               0.07076    0.04740   1.493   0.1360    
Income:Advertising  0.03748    0.01999   1.875   0.0613 .  
CompPrice:Income   -0.05181    0.02053  -2.524   0.0119 *  
Price:Age           0.01399    0.01991   0.703   0.4826    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4637 on 535 degrees of freedom
Multiple R-squared:  0.7593,    Adjusted R-squared:  0.753 
F-statistic: 120.5 on 14 and 535 DF,  p-value: < 2.2e-16

6.1 Variable Significance

  1. Which variables ( and interaction pairs ) are significant ? Do you find anything surprising or counterintuitive in the results? Describe any response variable or lack of response that seems surprising.
  1. Repeat the above but using the “leaps” package to perform subset selection, using the full model using ONLY the following variables:

Price Population CompPrice Income Education Age Advertising ShelveLoc

Plus the following interaction term: Income and Age Income and Advertising Income and CompPrice

Use the regsubsets command in “leaps” to do the full model ( do NOT specify really.big=T as it will require a long time to complete, along with forward and backward subset selection. Use the R file, Subset_select.R as a guide for syntax.

  1. Use the following model selection criteria to determine which of the resulting models is “best” according to RSS, adjusted R squared, Mallow’s Cp, and BIC.

The result of the regressions is a model object, so you can query the proper coefficient of the model to determine which model, of the multiple models fit, produces the best metric.

Create a table, or simply list, for the full model, and forward and backward selection, which model # is the best, then write out the model, that is put the model in the form y=intercept + coefficient*variable, for the best model using the BIC measure. NOTE: these may be the same model, or may differ slightly.

LS0tCnRpdGxlOiAiQ2Fyc2VhdHMgUiBOb3RlYm9vayIKYXV0aG9yOiAiQ29vcGVyIE0uIFN0YW5zYnVyeSAoNjM5MTk4NDQpIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgZmlnX2NhcHRpb246IHllcwogICAgaGlnaGxpZ2h0OiBweWdtZW50cwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRoZW1lOiBzYW5kc3RvbmUKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KRXhlY3V0ZTogKkNtZCtTaGlmdCtFbnRlciouIApOZXcgQ2h1bms6ICpDbWQrT3B0aW9uK0kqLgpQcmV2aWV3OiAqQ21kK1NoaWZ0K0sqCk5vdGVzOiBUaGUgcHJldmlldyBzaG93cyB5b3UgYSByZW5kZXJlZCBIVE1MIGNvcHkgb2YgdGhlIGNvbnRlbnRzIG9mIHRoZSBlZGl0b3IuIENvbnNlcXVlbnRseSwgdW5saWtlICpLbml0KiwgKlByZXZpZXcqIGRvZXMgbm90IHJ1biBhbnkgUiBjb2RlIGNodW5rcy4gSW5zdGVhZCwgdGhlIG91dHB1dCBvZiB0aGUgY2h1bmsgd2hlbiBpdCB3YXMgbGFzdCBydW4gaW4gdGhlIGVkaXRvciBpcyBkaXNwbGF5ZWQuCiMgSW50cm9kdWN0aW9uCgoKIyMgQ29uZmlndXJhdGlvbgoKYGBge3J9CmxpYnJhcnkobGVhcHMpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShtZ2N2KQpsaWJyYXJ5KERhdGFFeHBsb3JlcikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ0V4dHJhKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShwbG90bHkpCmxpYnJhcnkoY29ycnBsb3QpCmxpYnJhcnkoaHRtbHRvb2xzKQpgYGAKCiMjIFN5c3RlbSBJbmZvcm1hdGlvbgpEdWUgdG8gdGhlIGxhcmdlIG51bWJlciBvZiBsaWJyYXJpZXMgaW4gdXNlIEkgaGF2ZSBwcm92aWRlZCBzeXN0ZW0gaW5mb3JtYXRpb24uCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpzYXBwbHkoYygncmVwcicsICdJUmRpc3BsYXknLCAnSVJrZXJuZWwnKSwgZnVuY3Rpb24ocCkgcGFzdGUocGFja2FnZVZlcnNpb24ocCkpKQpgYGAKCiMgRGF0YQpGaXJzdCB3ZSBpbnNwZWN0IHRoZSByYXcgcmVjb3JkcyB1c2luZyBgYmFzaGAuIEFsd2F5cyBhIGdvb2QgaWRlYSB0byBsb29rIGF0IHRoaW5ncyBiZWZvcmUgeW91IG1hc2ggJ2VtIGludG8gYW4gSURFLgoKYGBge2Jhc2h9CmhlYWQgLW4gNSBkYXRhL0NhcnNlYXRzX29yZy5jc3YKYGBgCgpOb3cgSSBsb2FkIHRoZSBkYXRhIGludG8gYHJgLCBhbmQgZHJvcCB0aGUgIklEIiBjb2x1bW4uIAoKYGBge3J9CmNhcnNlYXRzIDwtIHJlYWQuY3N2KCJkYXRhL0NhcnNlYXRzX29yZy5jc3YiLCBoZWFkZXIgPSBULCBzdHJpbmdzQXNGYWN0b3JzID0gVCkKCmRyb3BzIDwtIGMoIlgiKQpjYXJzZWF0cyA8LSBjYXJzZWF0c1sgLCAhKG5hbWVzKGNhcnNlYXRzKSAlaW4lIGRyb3BzKV0KaGVhZChjYXJzZWF0cywgMTApCmBgYAoKSGVyZSBJIGNyZWF0ZSB0d28gbmV3IGRhdGFmcmFtZXMgdG8gbWFuYWdlIG51bWVyaWMgYW5kIGNhdGVnb3JpY2FsIGRhdGEuCgpgYGB7cn0KIyBnZXQgdmVjdG9ycyBvZiBjb250aW51b3VzIGFuZCBjYXRlZ29yaWNhbCBjb2xzCm51bXMgPC0gZHBseXI6OnNlbGVjdF9pZihjYXJzZWF0cywgaXMubnVtZXJpYykKY2F0cyA8LSBkcGx5cjo6c2VsZWN0X2lmKGNhcnNlYXRzLCBpcy5mYWN0b3IpCgpudW1zW3NhbXBsZShucm93KG51bXMpLCAxMCksIF0KCmNhdHNbc2FtcGxlKG5yb3coY2F0cyksIDEwKSwgXQpgYGAKCkxldCdzIGdldCBzb21lIHF1aWNrIHN1bW1hcmllcyBvZiBlYWNoOgoKYGBge3J9CnByaW50KCdOdW1lcmljIFN1bW1hcmllcycpCnN1bW1hcnkobnVtcykKcHJpbnQoJ0NhdGVnb3JpY2FsIFN1bW1hcmllcycpCnN1bW1hcnkoY2F0cykKYGBgCgpgYGB7cn0Kc3RyKG51bXMpCnN0cihjYXRzKQpgYGAKCgojIyBEYXRhIERpbWVuc2lvbmFsaXR5ClRoaXMgY29tbWFuZCBpcyB0byBpbnNwZWN0IHRoZSBkaWZmZXJlbnQgZGF0YSB0eXBlcyBpbiB0aGUgZGF0YS4gCgpgYGB7cn0Kc3RyKGNhcnNlYXRzKQpwcmludCgiIikKcHJpbnQocGFzdGUoJ051bWJlciBvZiBDb2x1bW5zOicsIG5jb2woY2Fyc2VhdHMpKSkKcHJpbnQocGFzdGUoJ051bWJlciBvZiBOdW1lcmljIENvbHVtbnM6JywgbmNvbChudW1zKSkpCnByaW50KHBhc3RlKCdOdW1iZXIgb2YgQ2F0ZWdvcmljYWwgQ29sdW1uczonLCBuY29sKGNhdHMpKSkKYGBgCgpIZXJlJ3MgYSBxdWljayB3YXkgdG8gZXhhbWluZSBnZW5lcmFsIHByb3BlcnRpZXMgb2YgdGhlIGRhdGE6CgpgYGB7cn0KRGF0YUV4cGxvcmVyOjppbnRyb2R1Y2UoZGF0YT1jYXJzZWF0cykKYGBgCgojIE51bWVyaWMgUGxvdHRpbmcKSSBzdGFydCBvdXQgd2l0aCBhIGZldyBnZW5lcmFsIHNjYXR0ZXIgcGxvdHMuCgpgYGB7cn0KcGxvdF9seShkYXRhPWNhcnNlYXRzLCAKICAgICAgICB4PX5BZ2UsIAogICAgICAgIHk9flNhbGVzLCAKICAgICAgICBtb2RlID0gJ21hcmtlcnMnLCAKICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCAKICAgICAgICBjb2xvcj1+U2hlbHZlTG9jKSAlPiUKICAgICAgICAgICAgbGF5b3V0KHRpdGxlID0gIkFnZSwgU2hlbGYgTG9jYXRpb24sIGFuZCBTYWxlcyBTY2F0dGVyIFBsb3QiLCB3aWR0aD05MDApIApgYGAKClRoaXMgcGxvdCBiZWxvdyBzaG93cyBnb29kIHNlcGFyYXRpb24gYW5kIGEgd2VhayBsaW5lYXIgdHJlbmQuIFRoZXNlIHZhcmlhYmxlcyBhcmUgd29ydGggaW52ZXN0aWdhdGluZy4KCmBgYHtyfQpwbG90X2x5KGRhdGE9Y2Fyc2VhdHMsIAogICAgICAgIHg9flByaWNlLCAKICAgICAgICB5PX5TYWxlcywgCiAgICAgICAgbW9kZSA9ICdtYXJrZXJzJywgCiAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgCiAgICAgICAgY29sb3I9flNoZWx2ZUxvYykgJT4lCiAgICAgICAgICAgIGxheW91dCh0aXRsZSA9ICJQcmljZSwgU2hlbGYgTG9jYXRpb24sIGFuZCBTYWxlcyBTY2F0dGVyIFBsb3QiLCB3aWR0aD05MDApCmBgYAoKSGVyZSB3ZSBpbnNwZWN0IHRoZSBkZW5zaXR5IG9mIHRoZSAnUHJpY2UgdnMgU2FsZXMnIHJlbGF0aW9uc2hpcDoKCmBgYHtyfQpwbG90X2x5KGRhdGE9Y2Fyc2VhdHMsIAogICAgICAgIHg9flByaWNlLCAKICAgICAgICB5PX5TYWxlcywgCiAgICAgICAgbW9kZSA9ICdtYXJrZXJzJywgCiAgICAgICAgc2l6ZSA9IH5QcmljZSwKICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLAogICAgICAgIGNvbG9ycyA9ICJEYXJrMiIsCiAgICAgICAgYWxwaGEgPSAuNikgJT4lCiAgICAgICAgICAgIGxheW91dCh0aXRsZSA9ICJQcmljZSwgVVMsIGFuZCBTYWxlcyBTY2F0dGVyIFBsb3QiLCB3aWR0aD05MDApIApgYGAKCiMgTm9ybWFsaXphdGlvbgpJIGNob29zZSB0byBub3JtYWxpemUgdGhlIG51bWVyaWMgZGF0YSBpbiBvcmRlciB0byBiZSBhYmxlIHRvIHBsb3QgZWFjaCB2YXJpYWJsZSBvbiB0aGUgc2FtZSBzY2FsZS4gVGhpcyB3aWxsIGFsbG93IG1lIHRvIGludmVzdGlnYXRlIHRoZSB2YXJpYXRpb24gb2YgZWFjaCBwcmVkaWN0b3IgcmVsYXRpY3ZlIHRvIFNhbGVzLgoKYGBge3J9CnByZU9iaiA8LSBwcmVQcm9jZXNzKG51bXMsIG1ldGhvZD1jKCJjZW50ZXIiLCAic2NhbGUiKSkKc2NhbGVkLm51bXMgPC0gcHJlZGljdChwcmVPYmosIG51bXMpCgpoZWFkKHNjYWxlZC5udW1zKQpgYGAKCmBgYHtyfQpzdHIoc2NhbGVkLm51bXMpCnByaW50KCIiKQpzdW1tYXJ5KHNjYWxlZC5udW1zKQpgYGAKCiMjIERpc3RyaWJ1dGlvbnMKSGVyZSBhcmUgc2NhbGVkIGRpc3RyaWJ1dGlvbnMgKGhpc3RvZ3JhbXMgYW5kIGRlbnNpdHkgcGxvdHMpIGZvciBlYWNoIG51bWVyaWMgdmFyaWFibGUsIGluY2x1ZGluZyBTYWxlcy4gVGhlIHZhcmlhYmxlcyByZWxhdGluZyB0byBtb25leSAoJCkgdGVuZCB0byBiZSBhcHByb3hpbWF0ZWx5IG5vcm1hbC4gTWFueSBvdGhlciB2YXJpYWJsZXMgdGVuZCB0byBiZSBhcHByb3hpbWF0ZWx5IHVuaWZvcm0sIHdoaWNoIGRvZXMgbm90IGJvZGUgd2VsbCBmb3IgdGhlaXIgcHJlZGljdGl2ZSBwb3dlci4KCmBgYHtyfQpzY2FsZWQubnVtcyAlPiUKICAgIHRpZHlyOjpnYXRoZXIoKSAlPiUgCiAgICAgICAgZ2dwbG90KGFlcyh4PXZhbHVlLHk9Li5kZW5zaXR5Li4pKSsKCiAgICAgICAgICAgIGdndGl0bGUoJ0Rpc3RyaWJ1dGlvbnMgb2YgQ29udGlub3VzIFZhcmlhYmxlcyAoc2NhbGVkKScpICsKCiAgICAgICAgICAgIGZhY2V0X3dyYXAofiBrZXksIHNjYWxlcyA9ICJmcmVlIikgKwogICAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShmaWxsPUkoIm9yYW5nZSIpLCBjb2w9SSgiYmxhY2siKSwgYmlucyA9IDUwKSArCgogICAgICAgICAgICBmYWNldF93cmFwKH4ga2V5LCBzY2FsZXMgPSAiZnJlZSIpICsKICAgICAgICAgICAgZ2VvbV9kZW5zaXR5KGNvbG9yPSJibHVlIiwgZmlsbD0nbGlnaHQgYmx1ZScsIGFscGhhID0gMC40KQpgYGAKCkhlcmUgd2UgcGxvdCBhbGwgbnVtZXJpYyB2YXJpYWJsZXMgYWdhaW5zdCB0aGVpciBkaXN0cmlidXRpb25zLiBUaGlzIGlzIGp1c3QgYW5vdGhlciB3YXkgdG8gZXhhbWluZSB0aGUgaW5mb3JtYXRpb24gc2hvd24gYWJvdmUuCgpgYGB7cn0Kc2NhbGVkLm51bXMgJT4lCiAgICB0aWR5cjo6Z2F0aGVyKCkgJT4lIAogICAgICAgIHBsb3RfbHkoeD1+a2V5LCB5PX52YWx1ZSwKICAgICAgICAgICAgICAgIHR5cGUgPSAiYm94IiwgCiAgICAgICAgICAgICAgICBib3hwb2ludHMgPSAiYWxsIiwgCiAgICAgICAgICAgICAgICBqaXR0ZXIgPSAwLjQsCiAgICAgICAgICAgICAgICBwb2ludHBvcyA9IDAsCiAgICAgICAgICAgICAgICBjb2xvciA9IH5rZXksCiAgICAgICAgICAgICAgICBjb2xvcnMgPSAiRGFyazIiKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIHN1YnBsb3Qoc2hhcmVYID0gVFJVRSkgICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5b3V0KHRpdGxlID0gIk51bWVyaWMgVmFyaWFibGUgRGlzdHJpYnV0aW9ucyAoc2NhbGVkKSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXM9bGlzdCh0aXRsZT0nU3RhbmRhcmQgRGV2aWF0aW9uJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4YXhpcz1saXN0KHRpdGxlPSdWYXJpYWJsZScpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXV0b3NpemU9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aWR0aD05MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQ9NTAwKQpgYGAKCiMjIFNjYXR0ZXJwbG90cwoKSGVyZSB3ZSBwbG90IGFsbCBudW1lcmljIHZhcmlhYmxlcyBhZ2FpbnN0IFNhbGVzIChzY2FsZWQpLiBUaGlzIGFsbG93cyB1cyB0byBpbnZlc3RpZ2F0ZSBwb3NzaWJsZSBsaW5lYXIgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoYXQgdmFyaWFibGUgYW5kIFNhbGVzLiBBcyBzaG93biBiZWxvdywgb25seSAnUHJpY2UnIGFwcGVhcnMgdG8gaGF2ZSBhIGxpbmVhciByZWxhdGlvbnNob3Agd29ydGggaW52ZXN0aWdhdGluZy4gVGhpcyB0b29rIG1lIHNvIGxvbmcgdG8gZmlndXJlIG91dC4gCgpgYGB7cn0KCm51bWVyaWMuc2NhdHRlcnBsb3RzIDwtIGh0bWx0b29sczo6dGFnTGlzdCgpCmNvdW50ID0gMQoKZm9yIChpIGluIG5hbWVzKHNjYWxlZC5udW1zWywtMV0pKSB7CiAgCiAgbnVtZXJpYy5zY2F0dGVycGxvdHNbW2NvdW50XV0gPC0gcGxvdF9seShzY2FsZWQubnVtcywgeD1zY2FsZWQubnVtc1ssaV0sIHk9c2NhbGVkLm51bXMkU2FsZXMsCiAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gJ1JkWWxHbicsCiAgICAgICAgICAgICAgICAgICAgbW9kZSA9ICdtYXJrZXJzJywKICAgICAgICAgICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLAogICAgICAgICAgICAgICAgICAgIHNpemUgPSBzY2FsZWQubnVtcyRTYWxlc14yLAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gc2NhbGVkLm51bXMkU2FsZXMsCiAgICAgICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChsaW5lID0gbGlzdChjb2xvciA9ICdibGFjaycsd2lkdGggPSAyKSksCiAgICAgICAgICAgICAgICAgICAgbmFtZT1wYXN0ZShpKSkgJT4lCiAgICAgICAgICBsYXlvdXQodGl0bGUgPSBwYXN0ZShpLCAidnMgU2FsZXMgKHNjYWxlZCk8YnI+U2l6ZT1TYWxlc14yIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5YXhpcz1saXN0KHRpdGxlPSdTYWxlcycpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeGF4aXM9bGlzdCh0aXRsZT1pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dsZWdlbmQgPSBGQUxTRSkKICAKICBjb3VudCA9IGNvdW50ICsgMQogIAp9CgpudW1lcmljLnNjYXR0ZXJwbG90cwoKYGBgCgpCeSBhZGRpbmcgbmFpdmUgcmVncmVzc2lvbiBsaW5lcyB0byBhIGZldyBzY2F0dGVycGxvdHMgd2UgY2FuIGNvbmZpcm0gb3VyIHN1c3BpY2lvbnM6CgpgYGB7cn0KZml0LlBvcCA8LSBsbShTYWxlcyB+IFBvcHVsYXRpb24sIGRhdGEgPSBzY2FsZWQubnVtcykKZml0LkFnZSA8LSBsbShTYWxlcyB+IEFnZSwgZGF0YSA9IHNjYWxlZC5udW1zKQpmaXQuQ29tcFByaWNlIDwtIGxtKFNhbGVzIH4gQ29tcFByaWNlLCBkYXRhID0gc2NhbGVkLm51bXMpCmZpdC5QcmljZSA8LSBsbShTYWxlcyB+IFByaWNlLCBkYXRhID0gc2NhbGVkLm51bXMpCgpyZWdyZXNzaW9uLnNjYXR0ZXJwbG90cyA8LSBodG1sdG9vbHM6OnRhZ0xpc3QoKQoKcmVncmVzc2lvbi5zY2F0dGVycGxvdHNbWzFdXSA8LSAgcGxvdF9seShzY2FsZWQubnVtcywgCiAgICAgICAgICB4ID0gflBvcHVsYXRpb24sCiAgICAgICAgICBuYW1lID0gJ1BvcHVsYXRpb24gdnMgU2FsZXMgUmVncmVzc2lvbiBMaW5lJykgJT4lIAogICAgICAgICAgICAgIGFkZF9tYXJrZXJzKHkgPSB+U2FsZXMsCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdQb3B1bGF0aW9uIHZzIFNhbGVzIE9ic2VydmF0aW9ucycpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFkZF9saW5lcyh4ID0gflBvcHVsYXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGZpdHRlZChmaXQuUG9wKSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXlvdXQodGl0bGUgPSAiUG9wdWxhdGlvbiB2cyBTYWxlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXM9bGlzdCh0aXRsZT0nU2FsZXMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzPWxpc3QodGl0bGU9J1BvcHVsYXRpb24nKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd2xlZ2VuZCA9IEZBTFNFKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgCgpyZWdyZXNzaW9uLnNjYXR0ZXJwbG90c1tbMl1dIDwtICBwbG90X2x5KHNjYWxlZC5udW1zLCAKICAgICAgICAgIHggPSB+QWdlLAogICAgICAgICAgbmFtZSA9ICdBZ2UgdnMgU2FsZXMgUmVncmVzc2lvbiBMaW5lJykgJT4lIAogICAgICAgICAgICAgIGFkZF9tYXJrZXJzKHkgPSB+U2FsZXMsCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdBZ2UgdnMgU2FsZXMgT2JzZXJ2YXRpb25zJykgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRkX2xpbmVzKHggPSB+QWdlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBmaXR0ZWQoZml0LkFnZSkpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5b3V0KHRpdGxlID0gIkFnZSB2cyBTYWxlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXM9bGlzdCh0aXRsZT0nU2FsZXMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzPWxpc3QodGl0bGU9J0FnZScpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93bGVnZW5kID0gRkFMU0UpCgpyZWdyZXNzaW9uLnNjYXR0ZXJwbG90c1tbM11dIDwtICBwbG90X2x5KHNjYWxlZC5udW1zLCAKICAgICAgICAgIHggPSB+Q29tcFByaWNlLAogICAgICAgICAgbmFtZSA9ICdDb21wUHJpY2UgdnMgU2FsZXMgUmVncmVzc2lvbiBMaW5lJykgJT4lIAogICAgICAgICAgICAgIGFkZF9tYXJrZXJzKHkgPSB+U2FsZXMsCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdDb21wUHJpY2UgdnMgU2FsZXMgT2JzZXJ2YXRpb25zJykgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRkX2xpbmVzKHggPSB+Q29tcFByaWNlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBmaXR0ZWQoZml0LkNvbXBQcmljZSkpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5b3V0KHRpdGxlID0gIkNvbXBQcmljZSB2cyBTYWxlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXM9bGlzdCh0aXRsZT0nU2FsZXMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzPWxpc3QodGl0bGU9J0NvbXBQcmljZScpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93bGVnZW5kID0gRkFMU0UpCgpyZWdyZXNzaW9uLnNjYXR0ZXJwbG90c1tbNF1dIDwtICBwbG90X2x5KHNjYWxlZC5udW1zLCAKICAgICAgICAgIHggPSB+UHJpY2UsCiAgICAgICAgICBuYW1lID0gJ1ByaWNlIHZzIFNhbGVzIFJlZ3Jlc3Npb24gTGluZScpICU+JSAKICAgICAgICAgICAgICBhZGRfbWFya2Vycyh5ID0gflNhbGVzLAogICAgICAgICAgICAgICAgICAgIG5hbWUgPSAnUHJpY2UgdnMgU2FsZXMgT2JzZXJ2YXRpb25zJykgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRkX2xpbmVzKHggPSB+UHJpY2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGZpdHRlZChmaXQuUHJpY2UpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxheW91dCh0aXRsZSA9ICJQcmljZSB2cyBTYWxlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXM9bGlzdCh0aXRsZT0nU2FsZXMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzPWxpc3QodGl0bGU9J1ByaWNlJykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dsZWdlbmQgPSBGQUxTRSkKCgpyZWdyZXNzaW9uLnNjYXR0ZXJwbG90cwpgYGAKCkxldCdzIGNvbXBhcmUgdGhlIHNsb3BlczoKCmBgYHtyfQpzY2FsZWQubnVtcyAlPiUKICAgIHBsb3RfbHkoeSA9IH5TYWxlcykgJT4lCiAgICAgIGFkZF9saW5lcyh4PSB+UG9wdWxhdGlvbiwgeSA9IGZpdHRlZChmaXQuUG9wKSwgCiAgICAgICAgICAgICAgICBuYW1lID0gImZpdC5Qb3Agc2xvcGUiLCBsaW5lID0gbGlzdChzaGFwZSA9ICJsaW5lYXIiKSkgJT4lCiAgICAgIGFkZF9saW5lcyh4PSB+QWdlLCB5ID0gZml0dGVkKGZpdC5BZ2UpLCAKICAgICAgICAgICAgICAgIG5hbWUgPSAiZml0LkFnZSBzbG9wZSIsIGxpbmUgPSBsaXN0KHNoYXBlID0gImxpbmVhciIpKSAlPiUKICAgICAgYWRkX2xpbmVzKHg9IH5Db21wUHJpY2UsIHkgPSBmaXR0ZWQoZml0LkNvbXBQcmljZSksIAogICAgICAgICAgICAgICAgbmFtZSA9ICJmaXQuQ29tcFByaWNlIHNsb3BlIiwgbGluZSA9IGxpc3Qoc2hhcGUgPSAibGluZWFyIikpICU+JQogICAgICBhZGRfbGluZXMoeD0gflByaWNlLCB5ID0gZml0dGVkKGZpdC5QcmljZSksIAogICAgICAgICAgICAgICAgbmFtZSA9ICJmaXQuUHJpY2Ugc2xvcGUiLCBsaW5lID0gbGlzdChzaGFwZSA9ICJsaW5lYXIiKSkgJT4lCiAgICAgICAgICAgIAogICAgbGF5b3V0KHRpdGxlID0gIlJlZ3Jlc3Npb24gTGluZXMgdnMgU2FsZXMgKHNjYWxlZCkiLAogICAgICAgICAgIGF1dG9zaXplPUZBTFNFLAogICAgICAgICAgIHdpZHRoPTkwMCwKICAgICAgICAgICB5YXhpcz1saXN0KHRpdGxlPSdTYWxlcycpLAogICAgICAgICAgIHhheGlzPWxpc3QodGl0bGU9J1NjYWxlZCBOdW1lcmljIFZhcmlhYmxlJykpCmBgYAoKSGVyZSdzIGEgcHJldHR5IGdyYXBoaWMgdGhhdCBkb2Vzbid0IGhlbHAgbWUgdW5kZXJzdGFuZCBhbnl0aGluZyBhYm91dCB0aGUgZGF0YS4KCmBgYHtyfQp5ID0gc2NhbGVkLm51bXMkU2FsZXMKeCA9IHNjYWxlZC5udW1zJFByaWNlCgpzIDwtIHN1YnBsb3QoCiAgcGxvdF9seSh4ID0geCwgY29sb3IgPSBJKCJibGFjayIpLCB0eXBlID0gJ2hpc3RvZ3JhbScpLCAKICBwbG90bHlfZW1wdHkoKSwgCiAgcGxvdF9seSh4ID0geCwgeSA9IHksIHR5cGUgPSAnaGlzdG9ncmFtMmRjb250b3VyJywgc2hvd3NjYWxlID0gRiksIAogIHBsb3RfbHkoeSA9IHksIGNvbG9yID0gSSgiYmxhY2siKSwgdHlwZSA9ICdoaXN0b2dyYW0nKSwKICBucm93cyA9IDIsIGhlaWdodHMgPSBjKDAuMiwgMC44KSwgd2lkdGhzID0gYygwLjgsIDAuMiksCiAgc2hhcmVYID0gVFJVRSwgc2hhcmVZID0gVFJVRSwgdGl0bGVYID0gRkFMU0UsIHRpdGxlWSA9IEZBTFNFKQoKbGF5b3V0KHMsIHNob3dsZWdlbmQgPSBGQUxTRSwgYXV0b3NpemU9RkFMU0UsCiAgICAgICAgICAgd2lkdGg9NzAwLAogICAgICAgICAgIGhlaWdodD01MDAsIAogICAgICAgICAgIHlheGlzPWxpc3QodGl0bGU9J1NhbGVzJyksCiAgICAgICAgICAgeGF4aXM9bGlzdCh0aXRsZT0nUHJpY2UnKSkKYGBgCgojIENhdGVnb3JpY2FsIFBsb3R0aW5nCkZpcnN0LCBsZXQncyBjcmVhdGUgYSBkYXRhZnJhbWUgdGhhdCB3ZSBjYW4gdXNlOgpgYGB7cn0KY2F0ZWdvcmljYWwuYnkuc2FsZXMgPSBjYmluZChTYWxlcyA9IHNjYWxlZC5udW1zJFNhbGVzLCBjYXRzKQpgYGAKCkhlcmUgd2UgY2FuIHNlZSBhbGwgY2F0ZWdvcmljYWwgYnkgU2FsZXMuIFdlIHN1c3BlY3RlZCB0aGF0ICdTaGVsdmVMb2MnIHdvdWxkIGJlIGltcG9ydGFudCBiYXNlZCBvbiBvbmUgb2YgdGhlIGVhcmx5IHNjYXR0ZXJwbG90cy4gSXQgc2VlbXMgdGhhdCB0aGlzIGlzIHRoZSBjYXNlLgoKYGBge3J9CgpjYXRlZ29yaWNhbC5ib3hwbG90cyA8LSBodG1sdG9vbHM6OnRhZ0xpc3QoKQpjb3VudCA9IDEKCmZvciAoaSBpbiBuYW1lcyhjYXRlZ29yaWNhbC5ieS5zYWxlc1ssLTFdKSkgewogIAogIGNhdGVnb3JpY2FsLmJveHBsb3RzW1tjb3VudF1dIDwtIHBsb3RfbHkoY2F0ZWdvcmljYWwuYnkuc2FsZXMsIHg9Y2F0ZWdvcmljYWwuYnkuc2FsZXNbLGldLCB5PWNhdGVnb3JpY2FsLmJ5LnNhbGVzJFNhbGVzLAogICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gImJveCIsIAogICAgICAgICAgICAgICAgICAgICAgICBib3hwb2ludHMgPSAiYWxsIiwKICAgICAgICAgICAgICAgICAgICAgICAgaml0dGVyID0gLjIsCiAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50cG9zID0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPWNhdGVnb3JpY2FsLmJ5LnNhbGVzWyxpXSwKICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzPSdTZXQxJywgCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU9cGFzdGUoaSkpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5b3V0KHRpdGxlID0gcGFzdGUoaSwgInZzIFNhbGVzIChzY2FsZWQpIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93bGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlheGlzPWxpc3QodGl0bGU9J1NhbGVzIFN0YW5kYXJkIERldmlhdGlvbicpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeGF4aXM9bGlzdCh0aXRsZT1pKSkKICAKICBjb3VudCA9IGNvdW50ICsgMQogIAp9CgpjYXRlZ29yaWNhbC5ib3hwbG90cwpgYGAKCkhlcmUncyB0aGUgc2FtZSB0aGluZywgYnV0IG1vcmUgbXVzaWNhbGx5OgoKYGBge3J9CgpjYXRlZ29yaWNhbC52aW9saW5zIDwtIGh0bWx0b29sczo6dGFnTGlzdCgpCmNvdW50ID0gMQoKZm9yIChpIGluIG5hbWVzKGNhdGVnb3JpY2FsLmJ5LnNhbGVzWywtMV0pKSB7CiAgCiAgY2F0ZWdvcmljYWwudmlvbGluc1tbY291bnRdXSA8LSBwbG90X2x5KGNhdGVnb3JpY2FsLmJ5LnNhbGVzLCB4PWNhdGVnb3JpY2FsLmJ5LnNhbGVzWyxpXSwgeT1jYXRlZ29yaWNhbC5ieS5zYWxlcyRTYWxlcywKICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSBjYXRlZ29yaWNhbC5ieS5zYWxlc1ssaV0sCiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAndmlvbGluJywKICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzPSdTZXQxJywgCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU9cGFzdGUoaSksCiAgICAgICAgICAgICAgICAgICAgICAgIGJveCA9IGxpc3QodmlzaWJsZSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICBtZWFubGluZSA9IGxpc3QodmlzaWJsZSA9IFRSVUUpKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXlvdXQoeGF4aXMgPSBsaXN0KHRpdGxlID0gIlVTIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlNhbGVzIix6ZXJvbGluZSA9IEZBTFNFKSkKCiAgCiAgY291bnQgPSBjb3VudCArIDEKICAKfQoKY2F0ZWdvcmljYWwudmlvbGlucwpgYGAKCiMgTGluZWFyIFJlZ3Jlc3Npb24KCkZpcnN0LCBsZXQncyBtZXJnZSB0aGUgZGF0YSBzZXQgaW50byBhIHNpbmdsZSBkYXRhZnJhbWUKYGBge3J9CnNjYWxlZC5tZXJnZWQgPC0gbWVyZ2Uoc2NhbGVkLm51bXMsIGNhdGVnb3JpY2FsLmJ5LnNhbGVzLCBieT0iU2FsZXMiKQoKaGVhZChzY2FsZWQubWVyZ2VkKQpgYGAKCkZpcnN0LCBsZXQncyBsb29rIGF0IHNvbWUgdGhpbmdzIHRoYXQgbWF5IGdpdmUgdXMgdHJvdWJsZS4gTHVja2lseSBpdCBsb29rcyBsaWtlIHRoZSBvbmx5IHNlcmlvdXMgY29ycmVsYXRpb24gaXMgd2l0aCBvdXIgZGVwZW5kZW50IHZhcmlhYmxlLgoKYGBge3J9CnJlcyA8LSBjb3Ioc2NhbGVkLm51bXMpCgpjb3JycGxvdChyZXMsIHR5cGUgPSAidXBwZXIiLCBvcmRlciA9ICJoY2x1c3QiLCAKICAgICAgICAgdGwuY29sID0gImJsYWNrIiwgdGwuc3J0ID0gNDUpCmBgYAoKSXQgYXBwZWFycyB0aGF0IHJlc2lkdWFscyBhcmUgcm91Z2hseSBzeW1tZXRyaWNhbCBhcm91bmQgMC4gVGhhdCdzIHN0cmFuZ2UuIE1vc3RseSBkdWUgdG8gYSByZWxhdGl2ZWx5IHBvb3Igb3ZlcmFsbCBmaXQuIE5vdGUgaG93IGNsb3NlIHRvIHplcm8gbW9zdCBvZiB0aGUgY29lZmZpY2llbnQgZXN0aW1hdGVzIGFyZS4KCmBgYHtyfQpzaW1wbGUubG0gPC0gbG0oU2FsZXN+LiwgZGF0YT1zY2FsZWQubWVyZ2VkKQpzdW1tYXJ5KHNpbXBsZS5sbSkKYGBgCgojIEludGVyYWN0aW9uIFRlcm1zCgpIZXJlIHdlIGRlZmluZSBhIG5ldyBtb2RlbCB3aXRoIHNvbWUgaW50ZXJhY3Rpb24gdGVybXM6IAogICAgYS4gIEluY29tZSBhbmQgQWR2ZXJ0aXNpbmcKICAgIGIuICBJbmNvbWUgYW5kIENvbXBQcmljZQogICAgYy4gICBQcmljZSBhbmQgQWdlCgoKYGBge3J9CmludGVyYWN0aW9uLmxtIDwtIGxtKFNhbGVzfi4gKyBJbmNvbWUqQWR2ZXJ0aXNpbmcgKyBJbmNvbWUqQ29tcFByaWNlICsgUHJpY2UqQWdlLCBkYXRhPXNjYWxlZC5tZXJnZWQpCmludGVyYWN0aW9uLnN1bW1hcnkgPC0gc3VtbWFyeShpbnRlcmFjdGlvbi5sbSkKCnByaW50KGludGVyYWN0aW9uLnN1bW1hcnkpCmBgYAoKIyMgVmFyaWFibGUgU2lnbmlmaWNhbmNlCgo2LiAgV2hpY2ggdmFyaWFibGVzICggYW5kIGludGVyYWN0aW9uIHBhaXJzICkgYXJlIHNpZ25pZmljYW50ID8gIERvIHlvdSBmaW5kIGFueXRoaW5nIHN1cnByaXNpbmcgb3IgY291bnRlcmludHVpdGl2ZSBpbiB0aGUgcmVzdWx0cz8gIERlc2NyaWJlIGFueSByZXNwb25zZSB2YXJpYWJsZSBvciBsYWNrIG9mIHJlc3BvbnNlIHRoYXQgc2VlbXMgc3VycHJpc2luZy4KCgpgYGB7cn0KCmBgYAoKNy4gIFJlcGVhdCB0aGUgYWJvdmUgYnV0IHVzaW5nIHRoZSDigJxsZWFwc+KAnSBwYWNrYWdlIHRvIHBlcmZvcm0gc3Vic2V0IHNlbGVjdGlvbiwgdXNpbmcgdGhlIGZ1bGwgbW9kZWwgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIHZhcmlhYmxlczoKClByaWNlClBvcHVsYXRpb24KQ29tcFByaWNlCkluY29tZQpFZHVjYXRpb24KQWdlCkFkdmVydGlzaW5nClNoZWx2ZUxvYwoKUGx1cyB0aGUgZm9sbG93aW5nIGludGVyYWN0aW9uIHRlcm06CkluY29tZSBhbmQgQWdlCkluY29tZSBhbmQgQWR2ZXJ0aXNpbmcKSW5jb21lIGFuZCBDb21wUHJpY2UKClVzZSB0aGUgcmVnc3Vic2V0cyBjb21tYW5kIGluIOKAnGxlYXBz4oCdIHRvIGRvIHRoZSBmdWxsIG1vZGVsICggZG8gTk9UIHNwZWNpZnkgcmVhbGx5LmJpZz1UIGFzIGl0IHdpbGwgcmVxdWlyZSBhIGxvbmcgdGltZSB0byBjb21wbGV0ZSwgYWxvbmcgd2l0aCBmb3J3YXJkIGFuZCBiYWNrd2FyZCBzdWJzZXQgc2VsZWN0aW9uLiAgIFVzZSB0aGUgUiBmaWxlLCBTdWJzZXRfc2VsZWN0LlIgYXMgYSBndWlkZSBmb3Igc3ludGF4LgoKCmBgYHtyfQpzY2FsZWQubWVyZ2VkLnNsaW0gPC0gc2NhbGVkLm1lcmdlZFsgLCAtd2hpY2gobmFtZXMoc2NhbGVkLm1lcmdlZCkgJWluJSBjKCJVUyIsIlVyYmFuIikpXQoKaW50ZXJhY3Rpb24ubG0uMiA8LSBsbShTYWxlc34uICsgSW5jb21lKkFkdmVydGlzaW5nICsgSW5jb21lKkNvbXBQcmljZSArIEluY29tZSpBZ2UsIGRhdGE9c2NhbGVkLm1lcmdlZC5zbGltKQppbnRlcmFjdGlvbi5zdW1tYXJ5IDwtIHN1bW1hcnkoaW50ZXJhY3Rpb24ubG0uMikKCnByaW50KGludGVyYWN0aW9uLnN1bW1hcnkkci5zcXVhcmVkKQoKcHJpbnQobmFtZXMoaW50ZXJhY3Rpb24uc3VtbWFyeSkpCiMgcGxvdChpbnRlcmFjdGlvbi5zdW1tYXJ5JHJzcyx4bGFiPSJOdW1iZXIgb2YgVmFyaWFibGVzIix5bGFiPSJSU1MiLHR5cGU9ImwiKQojIHBsb3QoaW50ZXJhY3Rpb24uc3VtbWFyeSRhZGpyMix4bGFiPSJOdW1iZXIgb2YgVmFyaWFibGVzIix5bGFiPSJBZGp1c3RlZCBSU3EiLHR5cGU9ImwiKQpgYGAKCjguICBVc2UgdGhlIGZvbGxvd2luZyBtb2RlbCBzZWxlY3Rpb24gY3JpdGVyaWEgdG8gZGV0ZXJtaW5lIHdoaWNoIG9mIHRoZSByZXN1bHRpbmcgbW9kZWxzIGlzIOKAnGJlc3TigJ0gYWNjb3JkaW5nIHRvIFJTUywgYWRqdXN0ZWQgUiBzcXVhcmVkLCBNYWxsb3figJlzIENwLCBhbmQgQklDLgoKVGhlIHJlc3VsdCBvZiB0aGUgcmVncmVzc2lvbnMgaXMgYSBtb2RlbCBvYmplY3QsIHNvIHlvdSBjYW4gcXVlcnkgdGhlIHByb3BlciBjb2VmZmljaWVudCBvZiB0aGUgbW9kZWwgdG8gZGV0ZXJtaW5lIHdoaWNoIG1vZGVsLCBvZiB0aGUgbXVsdGlwbGUgbW9kZWxzIGZpdCwgcHJvZHVjZXMgdGhlIGJlc3QgbWV0cmljLgoKQ3JlYXRlIGEgIHRhYmxlLCBvciBzaW1wbHkgbGlzdCwgZm9yIHRoZSBmdWxsIG1vZGVsLCBhbmQgZm9yd2FyZCBhbmQgYmFja3dhcmQgc2VsZWN0aW9uLCB3aGljaCBtb2RlbCAjIGlzIHRoZSBiZXN0LCB0aGVuIHdyaXRlIG91dCB0aGUgbW9kZWwsIHRoYXQgaXMgcHV0IHRoZSBtb2RlbCBpbiB0aGUgZm9ybSB5PWludGVyY2VwdCArIGNvZWZmaWNpZW50KnZhcmlhYmxlLCBmb3IgdGhlIGJlc3QgbW9kZWwgdXNpbmcgdGhlIEJJQyBtZWFzdXJlLiAgTk9URTogIHRoZXNlIG1heSBiZSB0aGUgc2FtZSBtb2RlbCwgb3IgbWF5IGRpZmZlciBzbGlnaHRseS4gCgoKCmBgYHtyfQoKYGBgCgpgYGB7cn0KCmBgYAoKYGBge3J9CgpgYGAKCmBgYHtyfQoKYGBgCgpgYGB7cn0KCmBgYAoKYGBge3J9CgpgYGAKCmBgYHtyfQoKYGBgCgoK